#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include "bmp280.h"
#include <linux/uaccess.h>

#define BMP280_TEMP_ADDR 0xfa
#define BMP280_PRESS_ADDR 0xf7


int bmp280_GetValue(struct i2c_client *client);

struct bmp280_dev
{
    atomic_t busy;
    dev_t bmp280DevNo;
    struct i2c_client *client;
    struct cdev bmp280_cdev;
    struct bmp280_dat bmp280_data;
};

static int bmp280_open(struct inode *bmp280_inode, struct file *fp)
{
    struct bmp280_dev *bmp280 = container_of(bmp280_inode->i_cdev, struct bmp280_dev, bmp280_cdev);
    fp->private_data = bmp280;
    
    return 0;
}

static int bmp280_close(struct inode *bmp280_inode, struct file *fp)
{
    return 0;
}

static long bmp280_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    struct bmp280_dev *bmp280 = fp->private_data;

	if (_IOC_TYPE(cmd) != BMP280_MAGIC)
		return -ENOTTY;

    switch (cmd)
    {
        case BMP280_GET:
            ret = bmp280_GetValue(bmp280->client);
            if (ret != 0)
            {
                return -EBUSY;
            }
            if (copy_to_user((struct bmp280_dat __user *)arg, &bmp280->bmp280_data, sizeof(struct bmp280_dat)))
            {
                return -EFAULT;
            }
            break;
        default: 
            return -ENOTTY;    
    }
    return sizeof(struct bmp280_dat);
}

struct file_operations bmp280_fops = {
    .owner = THIS_MODULE,
    .open = bmp280_open,
    .release = bmp280_close,
    .unlocked_ioctl = bmp280_ioctl,
};

short bmp280_MultipleReadTwo(struct i2c_client *client, unsigned char addr)
{
    unsigned short msb, lsb;
    short temp = 0;
    lsb = i2c_smbus_read_byte_data(client, addr);
    msb = i2c_smbus_read_byte_data(client, addr + 1);

    temp = msb << 8 | lsb;
    return temp;
}

int bmp280_MultipleReadThree(struct i2c_client *client, unsigned char addr)
{
    unsigned int msb, lsb, xlsb;
    int temp = 0;
    msb = i2c_smbus_read_byte_data(client, addr);
    lsb = i2c_smbus_read_byte_data(client, addr + 1);
    xlsb = i2c_smbus_read_byte_data(client, addr + 2);

    temp = (int)((msb << 12) | (lsb << 4) | (xlsb >> 4));

    return temp;
}

int bmp280_init(struct i2c_client *client)
{
    unsigned char msb, lsb;
    struct bmp280_dev *bmp280;
    bmp280 = i2c_get_clientdata(client);

    i2c_smbus_write_byte_data(client, 0xe0, 0xb6);
    i2c_smbus_write_byte_data(client, 0xf4, 0xff);
    i2c_smbus_write_byte_data(client, 0xf5, 0x00);


    lsb = i2c_smbus_read_byte_data(client, 0x88);
    msb = i2c_smbus_read_byte_data(client, 0x89);
    bmp280->bmp280_data.dig_T1 =  msb << 8 | lsb;//unsigned short

    bmp280->bmp280_data.dig_T2 = bmp280_MultipleReadTwo(client, 0x8A);
    bmp280->bmp280_data.dig_T3 = bmp280_MultipleReadTwo(client, 0x8C);

    lsb = i2c_smbus_read_byte_data(client, 0x8E);
    msb = i2c_smbus_read_byte_data(client, 0x8F);
    bmp280->bmp280_data.dig_P1 =  msb << 8 | lsb;//unsigned short

    bmp280->bmp280_data.dig_P2 = bmp280_MultipleReadTwo(client, 0x90);
    bmp280->bmp280_data.dig_P3 = bmp280_MultipleReadTwo(client, 0x92);
    bmp280->bmp280_data.dig_P4 = bmp280_MultipleReadTwo(client, 0x94);
    bmp280->bmp280_data.dig_P5 = bmp280_MultipleReadTwo(client, 0x96);
    bmp280->bmp280_data.dig_P6 = bmp280_MultipleReadTwo(client, 0x98);
    bmp280->bmp280_data.dig_P7 = bmp280_MultipleReadTwo(client, 0x9A);
    bmp280->bmp280_data.dig_P8 = bmp280_MultipleReadTwo(client, 0x9C);
    bmp280->bmp280_data.dig_P9 = bmp280_MultipleReadTwo(client, 0x9E);
    mdelay(200);

    return 0;
}

int bmp280_GetValue(struct i2c_client *client)
{
    struct bmp280_dev *bmp280;
    bmp280 = i2c_get_clientdata(client);

    bmp280->bmp280_data.adc_T = bmp280_MultipleReadThree(client, BMP280_TEMP_ADDR);
    bmp280->bmp280_data.adc_P = bmp280_MultipleReadThree(client, BMP280_PRESS_ADDR);

    if (bmp280->bmp280_data.adc_P == 0)
    {
        return -1;
    }

    return 0;
}

int bmp280_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret = 0;
    struct bmp280_dev *bmp280;
    printk( "bmp280_probe_success\r\n" );

    bmp280 = kmalloc(sizeof(struct bmp280_dev), GFP_KERNEL);
    if( !bmp280 )
    {
        ret = -ENOMEM;
        goto mem_err;
    }

    i2c_set_clientdata(client, bmp280);
    bmp280->client = client;

    /* 1.cdev初始化 */
    cdev_init(&bmp280->bmp280_cdev, &bmp280_fops);

    /* 2.申请设备号 */
    alloc_chrdev_region(&bmp280->bmp280DevNo, 0, 1, "BMP280");

    /* 3.将cdev添加到内核 */
    bmp280->bmp280_cdev.owner = THIS_MODULE;
    ret = cdev_add(&bmp280->bmp280_cdev, bmp280->bmp280DevNo, 1);
	if (ret)
    {
        goto add_err;
    }
		
    /* 4.bmp280初始化 */
    bmp280_init( client );

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		ret = -ENOSYS;
		goto fun_err;
	}
    
    return 0;
    fun_err:
        cdev_del(&bmp280->bmp280_cdev);
    add_err:
        kfree(bmp280);
    mem_err:
        unregister_chrdev_region(bmp280->bmp280DevNo, 1);
    
    return -1;
}

int bmp280_remove(struct i2c_client *client)
{
    struct bmp280_dev *bmp280;

    bmp280 = i2c_get_clientdata(client);
    cdev_del( &bmp280->bmp280_cdev );
    unregister_chrdev_region( bmp280->bmp280DevNo, 0 );

    kfree( bmp280 );
    return 0;
}

struct i2c_device_id bmp280_id[] = {
    {"bmp280",7},
    {}
};

struct of_device_id bmp280_of_match[] = {
    {.compatible = "bmp280",},
    {}
};

MODULE_DEVICE_TABLE( i2c, bmp280_id );

static struct i2c_driver bmp280_driver = {
    .probe = bmp280_probe,
    .remove = bmp280_remove,
    .id_table = bmp280_id,
    .driver = {
        .owner = THIS_MODULE,
        .name = "bmp280",
        .of_match_table = of_match_ptr( bmp280_of_match ),
    },
};

module_i2c_driver(bmp280_driver);

MODULE_LICENSE("GPL");